<?php

namespace Xin\WorkmanServer;

use ReflectionClass;
use Workerman\Worker;
use Xin\Support\Str;

class WorkerUtil
{
	/**
	 * @param string $workerName
	 * @param array $config
	 * @return Worker
	 */
	public static function start(string $workerName, array $config): Worker
	{
		$worker = new Worker($config['listen'] ?? null, $config['context'] ?? []);
		$worker->name = $workerName;

		return self::bindConfig($worker, $config);
	}

	/**
	 * @param Worker $worker
	 * @param array $config
	 * @return Worker
	 * @noinspection PhpDocMissingThrowsInspection
	 */
	public static function bindConfig(Worker $worker, array $config)
	{
		$workerReflectionClass = new ReflectionClass(Worker::class);

		foreach ($config as $key => $value) {
			$camelKey = Str::camel($key);
			if (property_exists($worker, $key)) {
				$worker->$key = $value;
			} elseif (property_exists($worker, $camelKey)) {
				$worker->$camelKey = $value;
			} elseif (Str::startsWith($key, ':')) {
				$camelKey = Str::camel(substr($key, 1));
				if (property_exists(Worker::class, $camelKey)) {
					// 设置静态变量的值
					/** @noinspection PhpUnhandledExceptionInspection */
					$property = $workerReflectionClass->getProperty($camelKey);
					$property->setValue(null, $value);
				}
			}
		}

		return self::bindHandler($worker, $config['handler'] ?? null);
	}

	/**
	 * @param Worker $worker
	 * @param string|array $handler
	 * @return Worker
	 */
	public static function bindHandler(Worker $worker, $handler): Worker
	{
		$worker->onWorkerStart = function (Worker $worker) use ($handler) {
			echo "Worker {$worker->name}({$worker->id}) Start.\n";

			// 如果 handler 为空，则不执行任何操作
			if (empty($handler)) {
				return;
			}

			// 如果 handler 是一个对象，则直接赋值给 $instance
			if (is_object($handler)) {
				$instance = $handler;
			} else { // 如果 handler 是一个类名，则实例化该类
				[$handlerClass, $constructorParams] = self::resolveWorkerHandlerConfig($handler);
				if (!class_exists($handlerClass)) {
					throw new \RuntimeException(
						"process error: class {$handlerClass} not exists\r\n"
					);
				}

				$instance = app($handlerClass, $constructorParams);
			}
			if (!$instance) {
				return;
			}

			$oldOnWorkerStop = $worker->onWorkerStop;
			$worker->onWorkerStop = function (Worker $worker) use ($oldOnWorkerStop, $instance) {
				$oldOnWorkerStop($worker);
				if (method_exists($instance, 'onWorkerStop')) {
					call_user_func([$instance, 'onWorkerStop'], $worker);
				}
			};

			// 绑定回调函数
			$callbackMap = [
				'onConnect',
				'onMessage',
				'onClose',
				'onError',
				'onBufferFull',
				'onBufferDrain',
				//                'onWorkerStop',
				'onWebSocketConnect',
				'onWorkerReload',
			];
			foreach ($callbackMap as $name) {
				if (method_exists($instance, $name)) {
					$worker->$name = [$instance, $name];
				}
			}

			// Worker Start Callback
			if (method_exists($instance, 'onWorkerStart')) {
				call_user_func([$instance, 'onWorkerStart'], $worker);
			}
		};

		// Worker Stop Callback
		$worker->onWorkerStop = function ($worker) {
			echo "Worker {$worker->name}({$worker->id}) Stop.\n";
		};

		return $worker;
	}

	/**
	 * @param array|string $handler
	 * @return array
	 */
	public static function resolveWorkerHandlerConfig($handler)
	{
		if (is_array($handler)) {
			$handlerClass = $handler['use'] ?? $handler['class'] ?? null;
			$constructorParams = $handler['constructor'] ?? [];
		} else {
			$handlerClass = $handler;
			$constructorParams = [];
		}

		return [$handlerClass, $constructorParams];
	}

	/**
	 * @param array $processConfig
	 * @return string
	 */
	public static function getWorkerName(array $processConfig)
	{
		[$handlerClass] = self::resolveWorkerHandlerConfig($processConfig['handler']);
		return class_basename($handlerClass);
	}
}
